﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.IO;
using System.Diagnostics;
using AdvanceLib.Core;
using AdvanceLib.ScriptCore;

namespace AdvanceLib.AddIn
{
    public class ExtensionManager : Singleton<ExtensionManager>
    {
        #region Constructors
        public ExtensionManager() : this(false) { }
        public ExtensionManager(bool load)
        {
            this.BaseDirectory = Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName);
            this.InitialDirectory = @"plugins";

            this.Providers = new List<IExtensionProvider>();
            this.SystemCommands = new List<ISystemCommand>();

            this.Load();
        }
        #endregion

        #region Properties
        public string InitialDirectory { get; set; }
        public string BaseDirectory { get; set; }

        public List<IExtensionProvider> Providers { get; set; }
        public List<ISystemCommand> SystemCommands { get; set; }

        public string CurrentDirectory { get { return Path.Combine(this.BaseDirectory, this.InitialDirectory); } }
        #endregion

        #region Methods
        #region Load Methods
        public void Load() { this.Load(this.InitialDirectory); }
        public void Load(string directory)
        {
            foreach (string fileName in Directory.GetFiles(directory, "*.dll"))
            {
                string pluginFile = Path.Combine(this.BaseDirectory, fileName);
                this.LoadFile(pluginFile);
            }
            foreach (string directoryName in Directory.GetDirectories(directory))
            {
                this.Load(directoryName);
            }
        }
        public void LoadFile(string fileName)
        {
            PluginLoader<IExtensionProvider> extensionLoader = new PluginLoader<IExtensionProvider>();
            PluginLoader<ISystemCommand> commandLoader = new PluginLoader<ISystemCommand>();

            Assembly asm = Assembly.LoadFile(fileName);

            extensionLoader.LoadPlugins(asm);
            commandLoader.LoadPlugins(asm);

            this.Providers.AddRange(extensionLoader.Plugins);
            this.SystemCommands.AddRange(commandLoader.Plugins);

            foreach (IExtensionProvider provider in extensionLoader.Plugins)
            {
                if (!provider.IsInstalled) provider.Install();
            }
        }
        #endregion

        #region Extension Methods
        public IExtensionProvider GetExtensionByName(string name)
        {
            if (this.HasExtension(name))
                return this.Providers.First(extension => extension.Name == name);
            else
                return null;
        }
        public TExtension GetExtension<TExtension>()
        {
            return (TExtension)this.GetExtension(typeof(TExtension));
        }
        public IExtensionProvider GetExtension(Type type)
        {
            if (this.HasExtension(type))
                return this.Providers.First(extension => extension.GetType() == type);
            else
                return null;
        }
        public bool HasExtension(string name)
        {
            return this.Providers.Count(extension => extension.Name == name) > 0;
        }
        public bool HasExtension<TExtension>()
        {
            return this.HasExtension(typeof(TExtension));
        }
        public bool HasExtension(Type type)
        {
            return this.Providers.Count(extension => extension.GetType() == type) > 0;
        }
        #endregion

        #region SystemCommand Methods
        public ISystemCommand GetSystemCommandByName(string name)
        {
            if (this.HasSystemCommand(name))
                return this.SystemCommands.First(command => command.Name == name);
            else
                return null;
        }
        public List<ISystemCommand> GetSystemCommandsByName(string name)
        {
            if (this.HasSystemCommand(name))
                return this.SystemCommands.Where(command => command.Name == name).ToList();
            else
                return null;
        }
        public TSystemCommand GetSystemCommand<TSystemCommand>()
        {
            return (TSystemCommand)this.GetSystemCommand(typeof(TSystemCommand));
        }
        public ISystemCommand GetSystemCommand(Type type)
        {
            if (this.HasSystemCommand(type))
                return this.SystemCommands.First(command => command.GetType() == type);
            else
                return null;
        }
        public bool HasSystemCommand(string name)
        {
            return this.SystemCommands.Count(command => command.Name == name) > 0;
        }
        public bool HasSystemCommand<TSystemCommand>()
        {
            return this.HasSystemCommand(typeof(TSystemCommand));
        }
        public bool HasSystemCommand(Type type)
        {
            return this.Providers.Count(extension => extension.GetType() == type) > 0;
        }
        #endregion
        #endregion
    }
}
